# -*- coding: utf-8 -*-
# @Time    : 2016/9/29

from sympy import solve, Eq, S, sympify
from mathsolver.functions.base import BaseSymbolValue, new_latex, BaseEq, BaseFunction, BaseAny
from mathsolver.functions.daoshu.daoshuyunsuan import HanShuDaoShuYunSuan
from mathsolver.functions.hanshu.helper import get_number_base_num_eq


class JiZhiDianQiuCan(BaseFunction):
    """已知函数的极大（小）值点求参数"""
    customise = "极值点"

    # 验证 val 是不是关于导函数 deriv 的极值点
    @staticmethod
    def test_jizhi(exp, var, val):
        assert exp.subs(var, sympify(val)) == 0
        from fractions import Fraction
        try:
            fractional_val = Fraction(val)
        except (TypeError, ValueError):
            fractional_val = val
        delta = Fraction('0.0000001')
        return exp.subs(var, fractional_val + delta) * exp.subs(var, fractional_val - delta) < 0

    def solver(self, *args):
        func = args[0]
        expr, var = func.expression, func.var
        # 极值点为 BaseEq, 或者 BaseNumber
        x_val = get_number_base_num_eq(args[1])

        # 补充：题目可能给定参数范围
        canshu_symbol = list(expr.free_symbols - {var})
        canshu_interval = S.Reals
        canshu_given = False
        if canshu_symbol:
            canshu_symbol.sort(key=lambda arg: str(arg))
            canshu_symbol = canshu_symbol[0]
            search_canshu_interval = self.search(canshu_symbol)
            if search_canshu_interval:
                canshu_interval = search_canshu_interval
                canshu_given = True

        # 函数的导数运算
        deriv = HanShuDaoShuYunSuan().solver(func).output[0]
        self.steps.append(["对函数求导得 {}'({}) = {}".format(func.name, func.var, new_latex(deriv.expression)), ""])

        # 将极值点代入导函数，列出方程（组）求出未知参数
        deriv_expr = deriv.expression.subs(func.var, x_val)

        eq1 = Eq(deriv_expr)
        if len(args) == 3:
            # 已知 f(x) 在 x = a 处的极值为 b
            if isinstance(args[2], BaseAny):
                y_val = args[2].sympify()
                eq2 = Eq(func.expression.subs(func.var, x_val) - y_val)
                results = solve([eq1, eq2], dict=True)
                self.steps.append(["", "根据 {1} 是函数的极值点得 {0}'({1}) = 0, {0}({1}) = {2}"
                                  .format(func.name, new_latex(x_val), new_latex(y_val))])
            # 已知 f(x) 在 x = a, x = b 处取到极值
            else:
                x_val2 = get_number_base_num_eq(args[2])
                eq2 = Eq(deriv.expression.subs(func.var, x_val2))
                results = solve([eq1, eq2], dict=True)
                self.steps.append(["根据函数在 x = {1}, x = {2} 处取到极值点得 {0}'({1}) = 0, {0}'({2}) = 0"
                                  .format(func.name, new_latex(x_val), new_latex(x_val2)), ""])

            # 补充：如果给定参数范围，filter 解
            if canshu_given:
                results = {k: v for k, v in results.items() if v in canshu_interval}

            str_collect = []
            for m in results:
                inner = ", ".join(["{} = {}".format(key, val) for key, val in m.items()])
                str_collect.append(inner)
            str_final = "解得 " + " 或 ".join(str_collect)
            self.steps.append(["", str_final])

            # 验证是否为极值点
            jizhis = []
            for result in results:
                if self.test_jizhi(deriv.expression.subs(result), deriv.var, str(x_val)):
                    jizhis.append(result)
                else:
                    str_int = ", ".join(["{} = {}".format(key, val) for key, val in result.items()])
                    self.steps.append(["根据导数在 {} = {} 两边符号，经检验 {} 非函数{}"
                                      .format(new_latex(func.var), new_latex(x_val), str_int, self.customise), ""])

            str_jizhi = " 或 ".join(
                [", ".join(["{} = {}".format(key, val) for key, val in jizhi.items()]) for jizhi in jizhis])
            self.steps.append(["所以 {}".format(str_jizhi), ""])

            self.output.append(BaseSymbolValue({tuple(list(jizhis[0].keys())): [tuple(list(jizhi.values())) for jizhi in jizhis]}))

        else:
            # 补充：如果含有1个以上参数，则输出极值点的关系式
            if len(deriv_expr.free_symbols) > 1:
                self.steps.append(["", "根据函数在 x = {0} 处取到极值点得 {1}'({0}) = 0".format(new_latex(x_val), func.name)])
                self.steps.append(["", "即：%s = 0" % new_latex(deriv_expr)])
                self.output.append(BaseEq([deriv_expr, 0]))
                self.label.add("已知极值点得到等式")
                return self
            else:
                result = solve(eq1, dict=True)

            var = list(result[0].keys())[0]
            vals = [value for item in result for key, value in item.items()]
            # 补充：如果给定参数范围，filter 解
            if canshu_given:
                vals = [val for val in vals if val in canshu_interval]

            self.steps.append(["根据函数在 x = {0} 处取到极值点得 {1}'({0}) = 0".format(new_latex(x_val), func.name),
                               "解得 " + " 或 ".join("{} = {}".format(new_latex(var), new_latex(val)) for val in vals)])

            # 验证是否为极值点
            jizhis = [val for val in vals if self.test_jizhi(deriv.expression.subs(var, val), deriv.var, x_val)]

            self.steps.append(["\n".join(["当 {} = {} 时 {}'({}) = {}"
                                         .format(new_latex(var), new_latex(val), func.name, new_latex(func.var),
                                                 new_latex(deriv.expression.subs(var, val))) for val in vals]), ""])

            self.steps.append(["检验导数在点两侧的符号可知，当 {} = {} 时 {} = {} 是函数的{}"
                              .format(new_latex(var),
                                      " 或 ".join([new_latex(jizhi) for jizhi in jizhis]),
                                      new_latex(func.var),
                                      new_latex(x_val),
                                      self.customise), ""])

            self.output.append(BaseSymbolValue({var: jizhis}))

        self.label.add("已知函数的极值点求参数")
        return self


class JiXiaoZhiDianQiuCan(JiZhiDianQiuCan):
    customise = "极小值点"

    # 验证 val 是不是关于导函数 deriv 的极小值点
    @staticmethod
    def test_jizhi(deriv, var, val):
        assert deriv.subs(var, val) == 0
        from fractions import Fraction
        try:
            fractional_val = Fraction(val)
        except (TypeError, ValueError):
            fractional_val = val
        delta = Fraction('0.0000001')
        return deriv.subs(var, fractional_val + delta) > 0 > deriv.subs(var, fractional_val - delta)


class JiDaZhiDianQiuCan(JiZhiDianQiuCan):
    customise = "极大值点"

    # 验证 val 是不是关于导函数 deriv 的极大值点
    @staticmethod
    def test_jizhi(deriv, var, val):
        assert deriv.subs(var, val) == 0
        from fractions import Fraction
        try:
            fractional_val = Fraction(val)
        except (TypeError, ValueError):
            fractional_val = val
        delta = Fraction('0.0000001')
        return deriv.subs(var, fractional_val + delta) < 0 < deriv.subs(var, fractional_val - delta)


if __name__ == '__main__':
    pass
